Afin de faire interagir ce programme avec l'utilisateur, il est nécessaire de pouvoir lui passer des arguments via la ligne de commande. Pour cela j'utilise la bibliothèque cmdargs fournie de manière standard avec la plateforme de développement Haskell-plateform.
Je ne détaillerais pas dans le détail l'API et le fonctionnement de cette bibliothèque. Je préciserai simplement que cette librairie possède deux méthodes de fonctionnement:
La méthode implicite : Simple et facile à prendre en main mais limitée en termes d'options.
La méthode explicite : Plus complexe mais contenant plus d'options et de souplesses pour les options à passer en ligne de commande.
De manière à avoir un meilleur contrôle sur le résultat final, je préfère utiliser la méthode explicite de description des arguments.
Un programme utilisant cmdargs fonctionne en appelant un mode de fonctionnement auquel sont attachées des options spécifiques.
Un mode est appelé par un mot clef placé juste après l'appel du nom du programme:
prog mode1 -a -v
prog mode2 -b --option
Pour chaque langue correspondra un mode de fonctionnement auquel s'ajoutera un mode pour pouvoir librement décrire son propre langage.
les différents modes de fonctionnement seront:
hsindex english ...
hsindex french ...
hsindex german ...
hsindex russian ...
hsindex custom ...
Pour tous les modes. les arguments suivants seront obligatoires:
--input
(ou -i
en version courte) Pour le fichier d'index à traiter.
Ces options seront facultatives
--style
(ou -s
en version courte) Pour ajouter un fichier de style pour la génération de l'index.
--output
(ou -o
en version courte) Pour définir le fichier de sortie (Un nom de fichier par défaut est défini pour chaque mode).
Les modes sont définis par le type de données MyModes
qui contient les constructeurs associés à chaque mode. Pour chaque constructeur on définit les différents champs qui seront utilisés par les options de la ligne de commande pour transmettre les valeurs au programme.
On définit donc
4 constructeurs (un pour chaque langue à générer)
1 constructeur pour une langue définit par l'utilisateur
2 modes supplémentaires pour afficher les informations de version et l'aide.
Note : Ces modes seront appelés via des options (-V
et -h
) et non avec des mots clefs.
data MyModes =
IndexRussian { fileIn :: FilePath -- ^ The input file
, fileOut :: FilePath -- ^ The output file
, fileStyle :: Maybe FilePath -- ^ The style file
}
| IndexFrench { fileIn :: FilePath
, fileOut :: FilePath
, fileStyle :: Maybe FilePath
}
| IndexGerman { fileIn :: FilePath
, fileOut :: FilePath
, fileStyle :: Maybe FilePath
}
| IndexEnglish { fileIn :: FilePath
, fileOut :: FilePath
, fileStyle :: Maybe FilePath
}
| IndexCustom { fileIn :: FilePath
, fileOut :: FilePath
, fileStyle :: Maybe FilePath
, fileDef :: FilePath
}
| ArgHelp
| ArgVersion
Les arguments de chaque langue seront gérés avec une fonction dédiée.
modeGenIndexFrench :: Mode MyModes
On utilise pour cela la fonction mode
qui prend comme argument
Le mot clef associé au mode.
Les valeurs initiales du mode.
Un descriptif du mode (Affiché dans l'aide).
Une fonction pour gérer les arguments sans noms (Ces types d'arguments ne seront pas utilisés ici).
Une liste de fonction permettant de gérer les options qui peuvent être utilisés avec ce mode.
Pour chaque option, on utilise la fonction flagReq
qui définit une option qui impose à l'utilisateur de fournir une valeur.
Cette fonction prend comme argument
Une liste de nom d'option ("input"
pour la version longue et "i"
pour la version courte).
Une fonction permettant de capturer la valeur requise.
Un complément d'information sur le type de valeur attendu (Affiché dans l'aide).
Les détails sur l'option (Affiché dans l'aide).
modeGenIndexFrench :: Mode MyModes
modeGenIndexFrench = mode "french" initialOptsIndexFrench description unnamedArg convertFlags
where
description = "Generate a French index"
unnamedArg = Arg {argValue = updateUnnamed, argType = "", argRequire = False}
where updateUnnamed str opts = Left ("Error unknown argument : " ++ str)
convertFlags =
[ flagReq ["input", "i"] setInputFile "<File>" "Input file"
, flagReq ["output", "o"] setOutpuFileFile "<File>" "Output file"
, flagReq ["style", "s"] setStyleFileFile "<File>" "Style file"
]
Les fonctions permettant de capturer les valeurs sont de type :
setInputFile :: String -> a -> Either String a
Ce qui signifie que ces fonctions prennent comme paramètres :
La valeur telle qu'elle est renseignée dans la ligne de commande sous forme de chaîne de caractères.
Le type de mode (ici MyModes
)
Un choix Either
entre
Une chaîne de caractère à retourner comme message d'erreur si la valeur n'est pas correcte.
Le constructeur du type MyModes
mis à jour en prenant en compte la valeur de l'option.
Ici, les valeurs à renseigner sont de simples chaînes de caractères contenant le chemin d'accès aux fichiers d'entrée, de sortie et de styles. Il n'y a donc pas besoin de faire des tests particuliers sur les chaînes. On retourne donc systématiquement Right
(Valeur correcte) avec une mise à jour de MyModes
setInputFile str opts = Right $ opts { fileIn = str }
On assemble les différents modes en créant un mode global dont le mot clef sera le nom du programme et le constructeur par défaut ArgHelp
pour lancer l'aide par défaut.
La liste des modes créés précédemment sera passé comme argument (mods
) et on ajoute des fonctions permettant de capturer les options associées à l'aide et aux informations de version.
La définition de modeGroupModes
en utilisant toGroup
permet d'ajouter la détection des arguments supplémentaires helpFlag
et versionFlag
. Ces fonctions sont créées avec flagNone
qui a le même rôle que flagReq
a ceci près que l'option ne nécessite pas qu'on lui fournisse une valeur.
const
est la fonction constante de type a -> b -> a
et sert à définir une fonction qui renvoie systématiquement la même valeur.
modesCLI mods = (modes "hsindex" ArgHelp "" mods) { modeGroupFlags = toGroup [helpFlag, versionFlag] }
where
helpFlag = flagNone ["help", "h", "?"] (const ArgHelp) "Help message"
versionFlag = flagNone ["version", "V"] (const ArgVersion) "Version informations"